﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace IntegratedOnBordDebug.UDPSocket
{

    /// <summary>
    /// upd server
    /// </summary>
    public sealed class FastUdpSocket
    {
        #region Private Members
        public int ReceiveTimes = 0;
        private readonly int _port;
        private readonly int _messageBufferSize;

        private Socket _socket = null;
        private AsyncSendPool _pool = null;
        public delegate void DelegateOnReceive(byte[] args, IPEndPoint IPEnd);
        public event DelegateOnReceive OnReceive;
        private readonly object objLockReceive = new object();

        #endregion

        #region Constructors
        /// <summary>
        /// new
        /// </summary>
        /// <param name="port"></param>
        /// <param name="protocol"></param>
        /// <param name="service"></param>
        public FastUdpSocket(int port) : this(port, 512)
        {
        }
        /// <summary>
        /// new
        /// </summary>
        /// <param name="port"></param>
        /// <param name="messageBufferSize"></param>
        public FastUdpSocket(int port, int messageBufferSize)
        {
            _port = port;
            _messageBufferSize = messageBufferSize;
        }
        #endregion

        #region Private Methods
        /// <summary>
        /// 异步接收数据
        /// </summary>
        /// <param name="e"></param>
        private void BeginReceive(SocketAsyncEventArgs e)
        {
            if (!_socket.ReceiveFromAsync(e))
                ThreadPool.QueueUserWorkItem(_ => ReceiveCompleted(this, e));
        }
        /// <summary>
        /// completed handle
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ReceiveCompleted(object sender, SocketAsyncEventArgs e)
        {
            Interlocked.Increment(ref ReceiveTimes);
            lock (objLockReceive)
            {
                if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
                {
                    try
                    {
                        //ThreadPool.QueueUserWorkItem(delegate
                        //{
                        OnReceive?.Invoke(new ArraySegment<byte>(e.Buffer, 0, e.BytesTransferred).Array, (IPEndPoint)e.RemoteEndPoint);
                        //Debug.WriteLine(string.Format("receive from {0} data : {1}", (IPEndPoint)e.RemoteEndPoint, Common.BytesToHex(new ArraySegment<byte>(e.Buffer, 0, e.BytesTransferred).Array)));
                        //});

                    }
                    catch (Exception ex)
                    {
                        Common.Log(string.Format("数据接收异常：{0}\r\n{1}", ex.Message, ex));
                    }
                }

                //receive again
                BeginReceive(e);
            }

        }
        #endregion

        #region IUdpServer Members
        /// <summary>
        /// start
        /// </summary>
        public void Start()
        {
            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
            _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
            _socket.Bind(new IPEndPoint(IPAddress.Any, _port));
            _socket.DontFragment = true;
            _socket.EnableBroadcast = true;

            _pool = new AsyncSendPool(_messageBufferSize, _socket);

            var e = new SocketAsyncEventArgs();
            e.RemoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
            e.SetBuffer(new byte[_messageBufferSize], 0, _messageBufferSize);
            e.Completed += ReceiveCompleted;
            BeginReceive(e);
        }
        /// <summary>
        /// stop
        /// </summary>
        public void Stop()
        {
            _socket.Close();
            _socket = null;
            _pool = null;
        }
        /// <summary>
        /// send to...
        /// </summary>
        /// <param name="endPoint"></param>
        /// <param name="payload"></param>
        public void SendTo(EndPoint endPoint, byte[] payload)
        {
            _pool.SendAsync(endPoint, payload);
        }
        #endregion

        /// <summary>
        /// 用于异步发送的<see cref="SocketAsyncEventArgs"/>对象池
        /// </summary>
        private class AsyncSendPool
        {
            #region Private Members
            private const int MAXPOOLSIZE = 3000;
            private readonly int _messageBufferSize;
            private readonly Socket _socket = null;
            private readonly ConcurrentStack<SocketAsyncEventArgs> _stack = new ConcurrentStack<SocketAsyncEventArgs>();
            #endregion

            #region Constructors
            /// <summary>
            /// new
            /// </summary>
            /// <param name="messageBufferSize"></param>
            /// <param name="socket"></param>
            public AsyncSendPool(int messageBufferSize, Socket socket)
            {
                if (socket == null) throw new ArgumentNullException("socket");
                _messageBufferSize = messageBufferSize;
                _socket = socket;
            }
            #endregion

            #region Private Methods
            /// <summary>
            /// send completed handle
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void SendCompleted(object sender, SocketAsyncEventArgs e)
            {
                Release(e);
            }
            #endregion

            #region Public Methods
            /// <summary>
            /// acquire
            /// </summary>
            /// <returns></returns>
            public SocketAsyncEventArgs Acquire()
            {
                SocketAsyncEventArgs e;
                if (_stack.TryPop(out e)) return e;

                e = new SocketAsyncEventArgs();
                e.SetBuffer(new byte[this._messageBufferSize], 0, this._messageBufferSize);
                e.Completed += this.SendCompleted;
                return e;
            }
            /// <summary>
            /// release
            /// </summary>
            /// <param name="e"></param>
            public void Release(SocketAsyncEventArgs e)
            {
                if (_stack.Count >= MAXPOOLSIZE)
                {
                    e.Completed -= SendCompleted;
                    e.Dispose();
                    return;
                }

                _stack.Push(e);
            }
            /// <summary>
            /// sned async
            /// </summary>
            /// <param name="endPoint"></param>
            /// <param name="payload"></param>
            /// <exception cref="ArgumentNullException">endPoint is null</exception>
            /// <exception cref="ArgumentNullException">payload is null or empty</exception>
            /// <exception cref="ArgumentOutOfRangeException">payload length大于messageBufferSize</exception>
            public void SendAsync(EndPoint endPoint, byte[] payload)
            {
                if (endPoint == null) throw new ArgumentNullException("endPoint");
                if (payload == null || payload.Length == 0) throw new ArgumentNullException("payload");
                if (payload.Length > _messageBufferSize)
                    throw new ArgumentOutOfRangeException("payload.Length", "payload length大于messageBufferSize");

                var e = Acquire();
                e.RemoteEndPoint = endPoint;

                Buffer.BlockCopy(payload, 0, e.Buffer, 0, payload.Length);
                e.SetBuffer(0, payload.Length);

                if (!_socket.SendToAsync(e))
                    ThreadPool.QueueUserWorkItem(_ => Release(e));
            }
            #endregion
        }
    }
}
